package com.gateway.app.common;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.lang.NonNull;
import org.springframework.util.StringUtils;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;

/**
 * gateway工具类.
 *
 * @author guoguifang
 * @version 1.8.2
 * @date 2022/5/27 1:53 下午
 */
@Slf4j
@UtilityClass
public class GatewayUtil {
    
    private static final Set<String> IP_PROXY_HEADER_KEY = new HashSet<>();
    
    private static final Set<String> HOST_PROXY_HEADER_KEY = new HashSet<>();
    
    static {
        IP_PROXY_HEADER_KEY.add("x-forwarded-for");
        IP_PROXY_HEADER_KEY.add("proxy-client-ip");
        IP_PROXY_HEADER_KEY.add("wl-proxy-client-ip");
        IP_PROXY_HEADER_KEY.add("http_client_ip");
        IP_PROXY_HEADER_KEY.add("http_x_forwarded_for");
        IP_PROXY_HEADER_KEY.add("x-real-ip");
        HOST_PROXY_HEADER_KEY.add("http-host");
        HOST_PROXY_HEADER_KEY.add("x-forwarded-host");
        HOST_PROXY_HEADER_KEY.add("x-real-host");
    }
    

    public static String formData2JsonData(@NonNull MediaType contentType, @NonNull String str) {
        Charset charset = contentType.getCharset() != null ? contentType.getCharset() : StandardCharsets.UTF_8;
        JSONObject jsonObject = new JSONObject();
        try {
            String[] pairs = StringUtils.tokenizeToStringArray(str, "&");
            for (String pair : pairs) {
                int idx = pair.indexOf('=');
                if (idx == -1) {
                    jsonObject.put(URLDecoder.decode(pair, charset.name()), null);
                } else {
                    String name = URLDecoder.decode(pair.substring(0, idx), charset.name());
                    String value = URLDecoder.decode(pair.substring(idx + 1), charset.name());
                    jsonObject.put(name, value);
                }
            }
        } catch (UnsupportedEncodingException e) {
            log.error("Can't encode message with charset '{}'!", charset.name());
            return str;
        }
        return JSON.toJSONString(jsonObject);
    }
    
    /**
     * 获取IP地址 使用Nginx等反向代理软件， 则不能通过request.getRemoteAddr()获取IP地址 如果使用了多级反向代理的话，X-Forwarded-For的值并不止一个，而是一串IP地址，X-Forwarded-For中第一个非unknown的有效IP字符串，则为真实IP地址
     */
    public static String getIpAddr(ServerHttpRequest request) {
        String ip = null;
        try {
            HttpHeaders headers = request.getHeaders();
            Set<String> headerNames = headers.keySet();
            for (String headerName : headerNames) {
                if (!StringUtils.isEmpty(headerName) && IP_PROXY_HEADER_KEY.contains(headerName.toLowerCase())) {
                    ip = checkAddress(headers.getFirst(headerName));
                    if (ip != null) {
                        break;
                    }
                }
            }
            if (ip == null) {
                ip = Optional.ofNullable(request.getRemoteAddress()).map(address -> address.getAddress().getHostAddress()).orElse(null);
            }
            if (ip != null) {
                //对于通过多个代理的情况，最后IP为客户端真实IP,多个IP按照','分割
                int position = ip.indexOf(",");
                if (position > 0) {
                    ip = ip.substring(0, position);
                }
            }
        } catch (Exception e) {
            log.error("Get ip error ", e);
        }
        return ip == null || ip.startsWith("0:0:0:0:0:0:0:1") ? "127.0.0.1" : ip;
    }
    
    public static String getHost(ServerHttpRequest request) {
        HttpHeaders headers = request.getHeaders();
        Set<String> headerNames = headers.keySet();
        for (String headerName : headerNames) {
            if (!StringUtils.isEmpty(headerName) && HOST_PROXY_HEADER_KEY.contains(headerName.toLowerCase())) {
                return headers.getFirst(headerName);
            }
        }
        return headers.getFirst(HttpHeaders.HOST);
    }
    
    public static boolean needSkipFilter(String path) {
        return path.endsWith("/favicon.ico");
    }
    
    private static String checkAddress(String ip) {
        if (StringUtils.isEmpty(ip)) {
            return null;
        }
        for (int i = 0; i < ip.length(); ++i) {
            char ch = ip.charAt(i);
            boolean valid = (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F') || (ch >= 'a' && ch <= 'f') || ch == '.' || ch == ':';
            if (!valid) {
                return null;
            }
        }
        return ip;
    }
    
}
